package com.thoughtworks.studentManagement;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.*;

import javax.persistence.Entity;
import javax.persistence.Id;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

@SpringBootApplication
public class StudentManagementApplication {

    enum Gender {
        Male,
        Female,
        Others
    }

    @Entity
    public class Student {
        private String name;
        private Gender gender;
        @Id
        private String studentNumber;
        private Date birthday;

        public Student() {
        }

        public Student(String name, String studentNumber) {
            this.name = name;
            this.studentNumber = studentNumber;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Gender getGender() {
            return gender;
        }

        public void setGender(Gender gender) {
            this.gender = gender;
        }

        public Date getBirthday() {
            return birthday;
        }

        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }

        public String getStudentNumber() {
            return studentNumber;
        }

        public void setStudentNumber(String studentNumber) {
            this.studentNumber = studentNumber;
        }

        @Override
        public String toString() {
            return "Student{" + "studentNumber=" + this.studentNumber + ", name='" + this.name + '\'' + '}';
        }
    }

    @Repository
    interface StudentRepository extends JpaRepository<Student, String> {
        @Modifying
        @Query("update Student s set s.name = :newName where s.studentNumber = :studentNumber")
        Student updateNameByStudentNumber(@Param("newName") String newName, @Param("studentNumber") String studentNumber);
    }

    @RestController
    class StudentController {

        private final StudentRepository repository;

        StudentController(StudentRepository repository) {
            this.repository = repository;
        }

        @GetMapping("/students")
        @ResponseStatus(value = HttpStatus.OK)
        List<Student> getAllStudents() {
            return repository.findAll();
        }

        @PostMapping("/students")
        Student createNewStudent(@RequestBody Student newStudent) {
            return repository.save(newStudent);
        }

        @GetMapping("/students/{studentNumber}")
        Student getOneStudent(@PathVariable String studentNumber) {
            return repository.findById(studentNumber)
                    .orElseThrow(() -> new StudentNotFoundException(studentNumber));
        }

        @PutMapping("/students/{studentNumber}")
        Student updateStudentInformation(@RequestBody Student newStudent, @PathVariable String studentNumber) {
            return repository.findById(studentNumber)
                    .map(student -> {
                        student.setName(newStudent.getName());
                        student.setBirthday(newStudent.getBirthday());
                        student.setGender(newStudent.getGender());
                        return repository.save(student);
                    })
                    .orElseThrow(() ->
                            new StudentNotFoundException(studentNumber));
        }

        // Oy, yes, PATCH was something I created for the initial HTTP/1.1 proposal
        // because partial PUT is never RESTful. ;-)
        //https://stackoverflow.com/questions/19732423/why-isnt-http-put-allowed-to-do-partial-updates-in-a-rest-api
        @PatchMapping("/students/{studentNumber}")
        Student partialUpdateStudentName(@RequestParam String newStudentName, @PathVariable String studentNumber) {
            return repository.findById(studentNumber)
                    .map(student -> {
                        student.setName(newStudentName);
                        return repository.updateNameByStudentNumber(newStudentName, studentNumber);
                    })
                    .orElseThrow(() ->
                            new StudentNotFoundException(studentNumber));
        }

        @DeleteMapping("/students/{studentNumber}")
        void deleteOneStudent(@PathVariable String studentNumber) {
            repository.deleteById(studentNumber);
        }
    }

    class StudentNotFoundException extends RuntimeException {
        StudentNotFoundException(String id) {
            super("Could not find student " + id);
        }
    }

    @ControllerAdvice
    class exceptionHandler {
        @ResponseBody
        @ExceptionHandler(StudentNotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        String employeeNotFoundHandler(StudentNotFoundException ex) {
            return ex.getMessage();
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(StudentManagementApplication.class, args);
    }

}
